﻿using UnityEngine;
using System.Collections;
using UniRx;
using System;
using System.IO;
using System.Collections.Generic;

namespace MGFramework {
    public class DownloadManager
    {
        /// <summary>
        /// 下载文件
        /// </summary>
        /// <param name="url">文件连接</param>
        /// <param name="filename">文件名</param>
        /// <param name="filetype">文件类型</param>
        /// <param name="path">存储位置</param>
        /// <param name="progress">进度回调</param>
        /// <param name="onError">错误回调</param>
        /// <param name="onComplete">成功回调</param>
        /// <param name="header">头，哈希值</param>
        /// <returns></returns>
        public static Download Download(string url, string filename,string filetype,string path, Action<float, string> progress, Action<string, Exception> onError = null, Action<string> onComplete = null, Dictionary<string, string> header = null)
        {
            if (filename != null && filetype != null) {
                url = url +  filename + "." + filetype;
                if (!Directory.Exists(path)) Directory.CreateDirectory(path);
                path = path + filename + "." + filetype;
            }
            FileStream stream = null;
            IDisposable disposable = ObservableWWW.GetAndGetBytes(url, header, new Progress<float>(progress, url)).Subscribe((byte[] bytes) => {
                if (null == stream)
                {
                    stream = File.OpenWrite(path);
                }

                if (null != stream)
                {
                    stream.Write(bytes, 0, bytes.Length);
                }
            },
            (Exception e) => {
                DebugConsole.Log(e.ToString());
                if (null != stream)
                {
                    stream.Flush();
                    stream.Close();
                }

                if (File.Exists(path))
                {
                    File.Delete(path);
                }

                if (null != onError)
                {
                    onError(url, e);
                }
            },
            () => {
                if (null != stream)
                {
                    stream.Flush();
                    stream.Close();
                }
                if (null != onComplete)
                {
                    onComplete(url);
                }
            });

            Download download = new Download();
            DownloadItem item = new DownloadItem(url, path, disposable);
            download.AddDownloadItemToQueue(item);

            return download;
        }

        /// <summary>
        /// 下载多个文件
        /// </summary>
        /// <param name="urls">文件连接</param>
        /// <param name="paths">存放位置</param>
        /// <param name="progress">进度回调</param>
        /// <param name="onComplete">成功回调</param>
        /// <param name="header">头，哈希值</param>
        /// <returns></returns>
        public static Download DownloadAll(string[] urls, string[] paths, Action<float, string[]> progress,Action<string[], string[]> onComplete = null, Dictionary<string, string> header = null)
        {
            Download downloads = new Download();
            int length = urls.Length;
            Dictionary<string, float> totalProgress = new Dictionary<string, float>();
            List<string> success = new List<string>();
            List<string> errors = new List<string>();
            for (int i = 0; i < length; i++)
            {
                totalProgress[urls[i]] = 0;
            }

            int respondCount = 0;
            System.Object lockObj = new System.Object();

            for (int i = 0; i < length; i++)
            {
                Download download = Download(urls[i],null,null, paths[i], (float value, string url) => {
                    totalProgress[url] = value;
                    if (null != progress)
                    {
                        float curTotalProgress = 0;
                        for (int j = 0; j < length; j++)
                        {
                            curTotalProgress += totalProgress[urls[j]];
                        }
                        progress(curTotalProgress / length, success.ToArray());
                    }
                },
                (string url, Exception e) => {
                    lock (lockObj)
                    {
                        respondCount++;
                    }
                    errors.Add(url);
                    if (respondCount >= length)
                    {
                        onComplete(success.ToArray(), errors.ToArray());
                    }
                },
                (string url) => {
                    lock (lockObj)
                    {
                        respondCount++;
                    }
                    success.Add(url);
                    if (respondCount >= length)
                    {
                        onComplete(success.ToArray(), errors.ToArray());
                    }
                }, header);

                downloads.AddDownloadToQueue(download);
            }

            return downloads;
        }
    }

    public class Progress<T> : IProgress<T>
    {
        private Action<T, string> progress;
        private string url;

        public Progress(Action<T, string> progress, string url)
        {
            this.progress = progress;
            this.url = url;
        }

        public void Report(T value)
        {
            if (null != this.progress)
            {
                this.progress(value, url);
            }
        }
    }

    public class Download
    {
        private Queue<DownloadItem> downloadItemQueue = null;

        public void AddDownloadItemToQueue(DownloadItem item)
        {
            if (null == item)
            {
                return;
            }

            if (null == downloadItemQueue)
            {
                this.downloadItemQueue = new Queue<DownloadItem>();
            }

            this.downloadItemQueue.Enqueue(item);
        }

        /**
         * 合并两个下载，把一个下载里面的任务合并到另外一个下载
         */
        public void AddDownloadToQueue(Download download)
        {
            if (null == download)
            {
                return;
            }

            if (null == this.downloadItemQueue)
            {
                this.downloadItemQueue = new Queue<DownloadItem>();
            }

            for (int i = 0, count = download.downloadItemQueue.Count; i < count; i++)
            {
                DownloadItem item = download.downloadItemQueue.Dequeue();
                this.downloadItemQueue.Enqueue(item);
            }
        }

        public void Cancel()
        {
            if (this.downloadItemQueue == null)
            {
                return;
            }

            for (int i = 0, count = this.downloadItemQueue.Count; i < count; i++)
            {
                DownloadItem item = this.downloadItemQueue.Dequeue();
                item.disposable.Dispose();
                if (File.Exists(item.path))
                {
                    File.Delete(item.path);
                }
            }
        }
    }

    public class DownloadItem
    {
        public string url;
        public string path;
        public IDisposable disposable;

        public DownloadItem(string url, string path, IDisposable disposable)
        {
            this.url = url;
            this.path = path;
            this.disposable = disposable;
        }
    }
}